﻿#include "service_layer.hh"
#include "zookeeper/service_finder_zookeeper.hh"
#include "../box/service_box.hh"
#include "../util/string_util.hh"
#include "../config/box_config.hh"
#include "lang_impl.hh"
#include "lang_impl.hh"
#include <algorithm>

kratos::service::ServicelLayer::ServicelLayer(ServiceBox* box) {
  box_ = box;  
}

kratos::service::ServicelLayer::~ServicelLayer() {
}

auto kratos::service::ServicelLayer::on_connect(const std::string& name,
    std::uint64_t channel_id)->void {
  std::vector<std::string> result;
  util::split(name, "-", result);
  if (result.size() != 2) {
    return;
  }
  const auto& service_name = result[0];
  const auto& host         = result[1];
  auto& host_info          = service_map_[service_name];
  auto& host_vec           = host_info.host_vec;
  if (host_info.host_vec.empty()) {
    host_info.index = 0;
  }
  host_vec.emplace_back(Host{host, channel_id});
  box_->write_log(
    lang::LangID::LANG_HOST_CONNECT_INFO,
    klogger::Logger::INFORMATION,
    service_name.c_str(),
    host.c_str(),
    (int)host_info.host_vec.size()
  );
}

auto kratos::service::ServicelLayer::on_close(const std::string& name,
    std::uint64_t channel_id)->void {
  std::vector<std::string> result;
  util::split(name, "-", result);
  if (result.size() != 2) {
    return;
  }
  const auto& service_name = result[0];
  const auto& host         = result[1];
  auto& host_vec           = service_map_[service_name].host_vec;
  for (auto it = host_vec.begin(); it != host_vec.end();) {
    if (it->channel_id == channel_id) {
      it = host_vec.erase(it);
      box_->write_log(
        lang::LangID::LANG_HOST_CONNECT_INFO,
        klogger::Logger::INFORMATION,
        service_name.c_str(),
        host.c_str(),
        (int)host_vec.size()
      );
    } else {
      it++;
    }
  }
}

auto kratos::service::ServicelLayer::get_channel(
  const std::string& service_name) -> std::uint64_t {
  auto root_it = service_map_.find(service_name);
  if (root_it == service_map_.end() || root_it->second.host_vec.empty()) {
    // 发现并连接到指定host
    std::list<std::string> new_hosts;
    if (!box_->get_service_finder()->find_service(service_name, new_hosts)) {
      return 0;
    }
    for (const auto& host : new_hosts) {
      connect_to_host(service_name, host);
    }
    return 0;
  } else {
    // 缓存内获取并轮询返回
    auto roller_index = root_it->second.index;
    if (roller_index >= root_it->second.host_vec.size()) {
      roller_index = 0;
    }
    auto ret = root_it->second.host_vec[roller_index];
    root_it->second.index += 1;
    return ret.channel_id;
  }
  // 添加变化监听器
  box_->get_service_finder()->add_listener(
    service_name,
    [&](const std::string& name, const std::vector<std::string>& hosts)->void {
      service_listener(name, hosts);
    }
  );
}

auto kratos::service::ServicelLayer::try_get_channel(
  const std::string& service_name) -> std::uint64_t {
  auto root_it = service_map_.find(service_name);
  if (root_it == service_map_.end() || root_it->second.host_vec.empty()) {
    return 0;
  }
  auto roller_index = root_it->second.index;
  if (roller_index >= root_it->second.host_vec.size()) {
    roller_index = 0;
  }
  auto ret = root_it->second.host_vec[roller_index];
  root_it->second.index += 1;
  return ret.channel_id;
}

auto kratos::service::ServicelLayer::get_remote_service() -> const ServiceMap& {
  return service_map_;
}

auto kratos::service::ServicelLayer::service_listener(const std::string& name,
  const std::vector<std::string>& hosts) -> void {
  std::vector<std::string> new_hosts;
  auto root_it = service_map_.find(name);
  if (root_it == service_map_.end()) {
    new_hosts = hosts;
  } else {
    const auto& host_vec = root_it->second.host_vec;
    for (const auto& host : hosts) {
      for (const auto& connected_host : host_vec) {
        if (connected_host.host == host) {
          continue;
        } else {
          new_hosts.emplace_back(host);
        }
      }
    }
  }
  // 连接到新的host
  for (const auto& host : new_hosts) {
    connect_to_host(name, host);
  }
}

auto kratos::service::ServicelLayer::connect_to_host(const std::string& name,
  const std::string& host)->bool {
  std::string ip;
  int port;
  // 获取配置
  if (!util::get_host_config(host, ip, port)) {
    // 配置格式错误
    box_->write_log(
      lang::LangID::LANG_BOX_SERVICE_ADDRESS_INCORRECT,
      klogger::Logger::FAILURE,
      host.c_str(),
      name.c_str()
    );
    return false;
  }
  auto timeout = box_->get_config().get_connect_other_box_timeout();
  // 连接到目标服务容器
  if (!box_->connect_to(name+"-"+host, ip, port, timeout)) {
    // 地址格式错误
    box_->write_log(
      lang::LangID::LANG_BOX_SERVICE_ADDRESS_INCORRECT,
      klogger::Logger::FAILURE,
      host.c_str(),
      name.c_str()
    );
    return false;
  }
  return true;
}
